zhouqijie

序、C++图像处理库

许多C++库可以用于读取和处理图像文件,常见的有SOIL2、Cimg、BoostGIL、Magick++等。

一、读取纹理文件

方法一:使用SOIL2:

//编写loadTexture函数:
GLuint loadTexture(const char * texImagePath)
{
	GLuint textureID;
	textureID = SOIL_load_OGL_texture(texImagePath, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
	if (textureID == 0) cout << "couldn't find texture file" << texImagePath << endl;
	return textureID;
}

其他方法:

有很多方法,比如用fopen()和fread()将数据从.bmp图像读入byte(unsigned char)数组。

二、创建纹理对象

方法一:使用SOIL2:

直接调用上面编写的loadTexture()函数,返回值就是纹理对象(整型ID标识)。

方法二:手动创建并复制数据:

//创建纹理对象
GLuint textureID;
glGenTexture(1, &textureID);
//复制数据
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);

三、纹理坐标

要将纹理应用于模型,需要为每个顶点指定纹理坐标。纹理坐标用于将模型上的点映射到纹理中的位置。(纹理中的像素被称为纹素(Texel)以便与屏幕的像素区分开)

四、纹理坐标载入缓冲区

纹理坐标单独通常设置一个顶点缓冲区,与三维坐标分开。

//...
float vertexTexCoords[24] = {
		0.0, 0.0, 1.0, 0.0, 0.5, 1.0,
		0.0, 0.0, 1.0, 0.0, 0.5, 1.0,
		0.0, 0.0, 1.0, 0.0, 0.5, 1.0,
		0.0, 0.0, 1.0, 0.0, 0.5, 1.0,
	};
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);//设置活跃缓冲区
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexTexCoords), vertexTexCoords, GL_STATIC_DRAW);//顶点数据加载进活跃缓冲区。
//...

五、应用纹理

渲染函数中把顶点属性传入着色器并激活纹理单元0:

void display(GLFWwindow * window, double currentTime)
{
    //...
    //把纹理坐标顶点属性传入着色器(location:1)
    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(1);
    //激活纹理单元
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    //...
}

在着色器中声明采样器变量并关联纹理单元0,然后采样颜色:

//*******************Vert Shader**********************
#version 430
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texcoords;
out vec2 uv;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;

//layout(binding = 0) uniform sampler2D samp;//顶点着色器一般用不到采样器

void main(void)
{ 
	gl_Position = proj_matrix * mv_matrix * vec4(position, 1.0);
	uv = texcoords;
}

//*******************Frag Shader**********************
#version 430

in vec2 uv;
out vec4 color;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;

layout(binding = 0) uniform sampler2D samp;

void main(void)
{
	color = texture(samp, uv); 
}
//****************************************************

六、多级渐远纹理(Mipmapping)

在信号处理中,采样不充分会产生走样现象。类似地,在纹理贴图采样中,当稀疏地采样高分辨率高细节图片时,会出现混乱随机的效果。如果纹理图像有重复图案,还有可能导致生成不同的图案。此时就需要使用多级渐远纹理贴图(Mipmapping)。

在编写的图片读取函数中添加:

GLuint loadTexture(const char * texImagePath)
{
	//...
	// MipMap
	glBindTexture(GL_TEXTURE_2D, textureID);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glGenerateMipmap(GL_TEXTURE_2D);
	//...
}

※GL_TEXTURE_MIN_FILTER参数可以设置的的缩小方法:

  1. GL_NEAREST_MIPMAP_NEAREST:采样单个Mipmap的最近纹素
  2. GL_NEAREST_MIPMAP_LINEAR:线性过滤(两个Mipmap的最近纹素插值)
  3. GL_LINEAR_MIPMAP_NEAREST:双线性过滤(单个Mipmap的4个纹素插值)
  4. GL_LINEAR_MIPMAP_LINEAR:三线性过滤(两个Mipmap的4个纹素插值)

七、各向异性过滤(Anisotropy filtering)

GLuint loadTexture(const char * texImagePath)
{
	//...
	// Aniso
	glBindTexture(GL_TEXTURE_2D, textureID);
	if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
	{
		GLfloat anisoset = 0.0f;
		glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoset);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoset);
	}
	//...

※glew的glewIsSupported()可以查询显卡是否支持AF。

八、环绕和平铺(Clamp/Repeat)

Wrap方式:Clamp、Repeat:

GLuint loadTexture(const char * texImagePath)
{
	glBindTexture(GL_TEXTURE_2D, textureID);
	//...
	// ClampOrRepeat
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	//...
}

glTexParameteri(GL_TEXTURE_2D, ??, ??)的可选参数:

  1. GL_TEXTURE_WRAP_S/T, GL_REPEAT: 直接忽略整数部分,平铺方式。(默认)
  2. GL_TEXTURE_WRAP_S/T, GL_MIRRORED_REPEAT:平铺方式,但整数部分是奇数时坐标反转。
  3. GL_TEXTURE_WRAP_S/T, GL_CLAMP_TO_EDGE:限定在0~1之间。

Wrap方式:BorderBorder:

GLuint loadTexture(const char * texImagePath)
{
	glBindTexture(GL_TEXTURE_2D, textureID);
	//...
	// Border
	float color[4] = {0.0, 0.0, 0.0, 1.0};
	glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color)
	//...
}

※还可以用glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, <color>)将外面的纹素设置为指定边框颜色。